home *** CD-ROM | disk | FTP | other *** search
/ PC Plus SuperCD (UK) 1999 January / PC Plus Super CD No55a (PCP-147A-1-99) (Disc 1) (1998).iso / linux / eznet / eznet.c next >
Encoding:
C/C++ Source or Header  |  1998-10-21  |  60.2 KB  |  2,296 lines

  1. /*
  2. ** Copyright (c) 1998 D. Richard Hipp
  3. **
  4. ** This program is free software; you can redistribute it and/or
  5. ** modify it under the terms of the GNU General Public
  6. ** License as published by the Free Software Foundation; either
  7. ** version 2 of the License, or (at your option) any later version.
  8. **
  9. ** This program is distributed in the hope that it will be useful,
  10. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12. ** General Public License for more details.
  13. ** 
  14. ** You should have received a copy of the GNU General Public
  15. ** License along with this program; if not, write to the
  16. ** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  17. ** Boston, MA  02111-1307, USA.
  18. **
  19. ** Author contact information:
  20. **   drh@acm.org
  21. **   http://www.hwaci.com/drh/
  22. */
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <ctype.h>
  26. #include <sys/types.h>
  27. #include <sys/stat.h>
  28. #include <sys/time.h>
  29. #include <termios.h>
  30. #include <unistd.h>
  31. #include <fcntl.h>
  32. #include <stdarg.h>
  33. #include <string.h>
  34. #include <signal.h>
  35. #include <sys/wait.h>
  36.  
  37. /*
  38. ** Names of files and executables and other hard-coded
  39. ** strings.
  40. */
  41. #define DB_DIR     "/var/eznet"
  42. #define DB_FILE    DB_DIR "/eznet.conf"
  43. #define PPP_DIR    "/etc/ppp"
  44. #define PPP_EXE    "/usr/sbin/pppd"
  45. #define SELF       "/usr/bin/eznet"
  46. #define DIALD_EXE  "/usr/sbin/diald"
  47. #define SESSIONLOG DB_DIR "/session.html"
  48.  
  49. /*
  50. ** The following define determines what version of PPP
  51. ** this program expects by default.  You can override this
  52. ** value using the pppversion= parameter.
  53. */
  54. #define DFLT_PPP_VERSION "2.3"
  55.  
  56. /*
  57. ** Each entry in the database is represented in memory by an instance
  58. ** of the following structure.
  59. */
  60. typedef struct ConfEntry ConfEntry;
  61. struct ConfEntry {
  62.   char *zLabel;       /* A label for this entry */
  63.   ConfEntry *next;    /* Next entry in the same record */
  64.   char *zValue;       /* Value for this entry */
  65. };
  66.  
  67. /*
  68. ** These variables record the whole database.  There are nRecord
  69. ** records, and each record consists of a linked list of ConfEntry
  70. ** structures where aRecord[N] points to the head of the list for
  71. ** record number N.
  72. */
  73. static int nRecord = 0;           /* Number of records */
  74. static ConfEntry **aRecord = 0;   /* List of ConfEntries for each record */
  75.  
  76. /*
  77. ** A transcript of the CHAT session is written to this file, if
  78. ** it is open.
  79. */
  80. static FILE *transcript = 0;
  81.  
  82. /*
  83. ** If this is a set-uid or set-gid process, then release all
  84. ** privileges.
  85. */
  86. static void Unprivileged(void){
  87.   int uid = getuid();
  88.   int gid = getgid();
  89.   setregid(gid,gid);
  90.   setreuid(uid,uid);
  91. }
  92.  
  93. /*
  94. ** Read the entire contents of a file into memory.  Space to
  95. ** hold the file is obtained from malloc.  NULL is returned if
  96. ** we can't read the file for any reason.
  97. */
  98. static char *ReadFile(const char *zFilename){
  99.   int fd;
  100.   FILE *in;
  101.   int toread;
  102.   int read;
  103.   int n;
  104.   char *zBuf;
  105.   struct stat sbuf;
  106.  
  107.   in = fopen(zFilename,"r");
  108.   if( in==0 ) return 0;
  109.   fd = fileno(in);
  110.   if( fstat(fd, &sbuf)!=0 ){
  111.     fclose(in);
  112.     return 0;
  113.   }
  114.   toread = sbuf.st_size;
  115.   read = 0;
  116.   zBuf = malloc( toread+1 );
  117.   if( zBuf==0 ){
  118.     fclose(in);
  119.     return 0;
  120.   }
  121.   while( toread && (n=fread(&zBuf[read],1,toread,in))>0 ){
  122.     toread -= n;
  123.     read += n;
  124.   }
  125.   zBuf[read] = 0;
  126.   fclose(in);
  127.   return zBuf;
  128. }
  129.  
  130. /* Change the status for a record.  The status is recorded in
  131. ** a file named
  132. **
  133. **       /var/eznet/status.NNN
  134. **
  135. ** where NNN is the record number.
  136. */
  137. static void WriteStatus(int iRec, char *zStatus, ...){
  138.   FILE *out;
  139.   va_list ap;
  140.   char zFilename[sizeof(DB_DIR)+20];
  141.  
  142.   sprintf(zFilename,"%s/status.%d",DB_DIR,iRec);
  143.   out = fopen(zFilename,"w");
  144.   if( out ){
  145.     time_t now;
  146.     struct tm *p;
  147.     char zBuf[200];
  148.     fchown(fileno(out),0,0);
  149.     fchmod(fileno(out),0644);
  150.     va_start(ap, zStatus);
  151.     vfprintf(out,zStatus,ap);
  152.     va_end(ap);
  153.     time(&now);
  154.     p = localtime(&now);
  155.     strftime(zBuf,sizeof(zBuf)-1," %I:%M%p %a %b %d, %Y\n", p);
  156.     fprintf(out,zBuf);
  157.     fclose(out);
  158.   }
  159. }
  160.  
  161. /* Write a status report from Chat.  This is written to a file
  162. ** named
  163. **
  164. **     /var/eznet/chatstat.NNN
  165. **
  166. ** where NNN is the record number.
  167. */
  168. static void WriteChatStat(int iRec, char *zStatus, ...){
  169.   FILE *out;
  170.   va_list ap;
  171.   char zFilename[sizeof(DB_DIR)+20];
  172.  
  173.   sprintf(zFilename,"%s/chatstat.%d",DB_DIR,iRec);
  174.   out = fopen(zFilename,"w");
  175.   if( out ){
  176.     va_start(ap, zStatus);
  177.     vfprintf(out,zStatus,ap);
  178.     va_end(ap);
  179.     fclose(out);
  180.   }
  181. }
  182.  
  183. /*
  184. ** Write text into a file.  zFile is the name of the file into
  185. ** which the text is written.  zFormat is a printf-like format
  186. ** string that defines the text.  The owner of the file is converted
  187. ** to root and the permissions are set to "mode".
  188. */
  189. static void WriteToFile(char *zFile, int mode, char *zFormat, ...){
  190.   FILE *out;
  191.   va_list ap;
  192.  
  193.   out = fopen(zFile,"w");
  194.   if( out==0 ) return;
  195.   fchmod(fileno(out),mode);
  196.   fchown(fileno(out),0,0);
  197.   va_start(ap,zFormat);
  198.   vfprintf(out,zFormat,ap);
  199.   va_end(ap);
  200.   fclose(out);
  201. }
  202.  
  203. /* Make sure /etc/ppp/ip-up and /etc/ppp/ip-down invoke eznet
  204. ** with either the "ipup" or "ipdown" argument (as approprite.)
  205. ** This is needed so that we can adjust the status when the link
  206. ** goes up or down.
  207. */
  208. static void SetIpUpDown(char *zUpDown){
  209.   char *zText;
  210.   int i;
  211.   int lineLen = 0;
  212.   int patLen;
  213.   char zPat[100];
  214.   char zFile[sizeof(PPP_DIR)+20];
  215.  
  216.   sprintf(zFile,"%s/ip-%s",PPP_DIR,zUpDown);
  217.   zText = ReadFile(zFile);
  218.   if( zText==0 ){
  219.     WriteToFile(zFile, 0744, 
  220.       "#!/bin/sh\n" SELF " ip%s $* &\n", zUpDown);
  221.     return;
  222.   }
  223.   for(i=0; zText[i] && zText[i]!='\n'; i++){}
  224.   if( zText[i]=='\n' ){
  225.     lineLen = i;
  226.   }
  227.   sprintf(zPat,SELF " ip%s $* &",zUpDown);
  228.   patLen = strlen(zPat);
  229.   while( zText[i] && (zText[i]!='/' || strncmp(&zText[i], zPat, patLen)) ){
  230.     i++;
  231.   }
  232.   if( zText[i]==0 ){
  233.     WriteToFile(zFile, 0744, "#!/bin/sh\n%s\n%s",
  234.       zPat, &zText[lineLen]);
  235.   }
  236. }
  237.  
  238. /*
  239. ** Parse a single line of the pap-secrets or chap-secrets file whose
  240. ** text begins as *pzText.  The elements of the line are place in
  241. ** azArgs[0] through azArgs[3].
  242. **
  243. ** After parsing, *pzText is left pointing to the first character
  244. ** after then end of the parsed text.  At end of file, *pzText points
  245. ** to 0.
  246. **
  247. ** All of azArgs[0] through azArgs[3] are always filled in.  Empty
  248. ** strings are inserted if necessary.  A comment line is placed in
  249. ** azArgs[0] and the others are empty strings.
  250. **
  251. ** Quotes and escapes are removed from azArgs[0] through azArgs[2],
  252. ** except for comment lines which are passed through unaltered.
  253. */
  254. static void GetSecret(char **pzText, char **azArgs){
  255.   int i;
  256.   char *z = *pzText;
  257.   while( isspace(*z) ) z++;
  258.   azArgs[0] = azArgs[1] = azArgs[2] = azArgs[3] = "";
  259.   if( *z=='#' ){
  260.      azArgs[0] = z;
  261.      while( *z && *z!='\n' ) z++;
  262.      if( *z=='\n' ){
  263.        *z = 0;
  264.        z++;
  265.      }
  266.      *pzText = z;
  267.      return;
  268.   }
  269.   for(i=0; i<=2; i++){
  270.     int hasEscape = 0;
  271.     azArgs[i] = z;
  272.     if( *z=='"' ){
  273.       azArgs[i]++;
  274.       z++;
  275.       while( *z && *z!='"' ){ 
  276.         if( *z=='\\' && z[1] ){ z++; hasEscape = 1; }
  277.         z++;
  278.       }
  279.       if( *z=='"' ){
  280.         *z = 0;
  281.         z++;
  282.       }
  283.     }else{
  284.       while( *z && !isspace(*z) ){
  285.         if( *z=='\\' && z[1] ){ z++; hasEscape = 1; }
  286.         z++;
  287.       }
  288.       if( *z ){
  289.         *z = 0;
  290.         z++;
  291.       }
  292.     }
  293.     while( isspace(*z) && *z!='\n' ){ z++; }
  294.     if( hasEscape ){
  295.       int j = 0, k = 0;
  296.       char *zStr = azArgs[i];
  297.       while( zStr[j] ){
  298.         if( zStr[j]=='\\' && zStr[j+1] ){ j++; }
  299.         zStr[k++] = zStr[j++];
  300.       }
  301.     }
  302.   }
  303.   azArgs[i] = z;
  304.   while( *z && *z!='\n' ){
  305.     if( *z=='\\' && z[1] ){ z++; }
  306.     z++;
  307.   }
  308.   if( *z=='\n' ){
  309.     *z = 0;
  310.     z++;
  311.   }
  312.   *pzText = z;
  313. }
  314.  
  315. /*
  316. ** The elements azArgs[0] through azArgs[3] contain the text of
  317. ** a secret.  This secret is written to *pzBuf.  *pzBuf is updated
  318. ** to point to the null terminator at the end of the written text.
  319. **
  320. ** Quotes and escapes are inserted on azArgs[0] through azArgs[2]
  321. ** if needed.  Except if the first character of azArgs[0] is "#"
  322. ** then no quotes or escapes are inserted.
  323. */
  324. static void AppendSecret(char **pzBuf, char **azArgs){
  325.   char *z = *pzBuf;
  326.   char *zSrc;
  327.   int i;
  328.   if( azArgs[0][0]=='#' ){
  329.     zSrc = azArgs[0];
  330.     while( *zSrc ){ *(z++) = *(zSrc++); }
  331.     *(z++) = '\n';
  332.     *z = 0;
  333.     *pzBuf = z;
  334.     return;
  335.   }
  336.   for(i=0; i<=2; i++){
  337.     zSrc = azArgs[i];
  338.     if( *zSrc==0 ) continue;
  339.     while( *zSrc ){
  340.       switch( *zSrc ){
  341.         case ' ':
  342.         case '\t':
  343.         case '\n':
  344.         case '"':
  345.         case '\\':
  346.           *(z++) = '\\';
  347.           /* Fall thru */
  348.         default:
  349.           *(z++) = *(zSrc++);
  350.           break;
  351.       }
  352.     }
  353.     *(z++) = ' ';
  354.   }
  355.   while( *zSrc ){ *(z++) = *(zSrc++); }
  356.   *(z++) = '\n';
  357.   *z = 0;
  358.   *pzBuf = z;
  359. }
  360.  
  361. /* Make sure the correct password for the service and user is found in the
  362. ** secrets file.  Add it if is not.  Preserve any commands or
  363. ** unrelated secrets that are previously found in the file.
  364. **
  365. ** Needless to say, we have to have root privileges to run
  366. ** this function successfully.  The secrets files contain information
  367. ** which should not be publicly viewable.
  368. */
  369. static void SetSecrets(
  370.   char *zBase,         /* Either "pap-secrets" or "chap-secrets" */
  371.   char *zService,      /* Name of the service provider we are calling */
  372.   char *zUser,         /* Our login name */
  373.   char *zPassword      /* Our password */
  374. ){
  375.   char *zOld, *zOldOrig;
  376.   char *z;
  377.   char *zNew;
  378.   int extra;
  379.   int seenServiceUser = 0;
  380.   int seenUserService = 0;
  381.   char *azArgs[4];
  382.   char zFile[sizeof(PPP_DIR)+20];
  383.  
  384.   if( zPassword==0 || zUser==0 || zService==0 || zFile==0 ) return;
  385.   sprintf(zFile,"%s/%.15s",PPP_DIR,zBase);
  386.   zOldOrig = zOld = ReadFile(zFile);
  387.   extra = (strlen(zService) + strlen(zPassword) + strlen(zUser) + 10)*4;
  388.   if( zOld==0 ){
  389.     zNew = malloc( extra + 100 );
  390.     sprintf(zNew,"#\n%s %s %s\n%s %s %s\n",
  391.        zService, zUser, zPassword,
  392.        zUser, zService, zPassword
  393.     );
  394.     WriteToFile(zFile, 0600, zNew);
  395.     free(zNew);
  396.     return;
  397.   }
  398.   z = zNew = malloc( strlen(zOld) + extra + 100 );
  399.   if( z==0 ) return;
  400.   while( zOld && *zOld ){
  401.     GetSecret(&zOld, azArgs);
  402.     if( azArgs[0][0]!='#' ){
  403.       if( strcmp(azArgs[0],zService)==0 && strcmp(azArgs[1],zUser)==0 ){
  404.         seenServiceUser = 1;
  405.         azArgs[2] = zPassword;
  406.         azArgs[3] = "";
  407.       }else if( strcmp(azArgs[0],zUser)==0 && strcmp(azArgs[1],zService)==0 ){
  408.         seenUserService = 1;
  409.         azArgs[2] = zPassword;
  410.         azArgs[3] = "";
  411.       }
  412.     }
  413.     AppendSecret(&z, azArgs);
  414.   }
  415.   if( !seenServiceUser ){
  416.     azArgs[0] = zService;
  417.     azArgs[1] = zUser;
  418.     azArgs[2] = zPassword;
  419.     azArgs[3] = "";
  420.     AppendSecret(&z, azArgs);
  421.   }
  422.   if( !seenUserService ){
  423.     azArgs[0] = zUser;
  424.     azArgs[1] = zService;
  425.     azArgs[2] = zPassword;
  426.     azArgs[3] = "";
  427.     AppendSecret(&z, azArgs);
  428.   }
  429.   WriteToFile(zFile, 0600, zNew);
  430.   free(zOldOrig);
  431.   free(zNew);
  432. }
  433.  
  434. /* zFile[*pI] is the first character in a line of text.  Return
  435. ** a pointer to the first non-whitespace character in this line.
  436. ** Also convert the \n into a \0 and make *pI be the index of
  437. ** the first character on the next line.
  438. **
  439. ** If zFile[*pI] is 0, then we've reached the end of input.
  440. ** Return NULL.
  441. */
  442. static char *GetLine(char *zFile, int *pI){
  443.   int i = *pI;
  444.   int iStart;
  445.   if( zFile[i]==0 ) return 0;
  446.   while( isspace(zFile[i]) ){ i++; }
  447.   iStart = i;
  448.   while( zFile[i] && zFile[i]!='\n' ){ i++; }
  449.   if( zFile[i] ){
  450.     zFile[i] = 0;
  451.     *pI = i+1;
  452.   }else{
  453.     *pI = i;
  454.   }
  455.   return &zFile[iStart];
  456. }
  457.  
  458. /* This array maps hexadecimal digit values into their numeric
  459. ** value.
  460. */
  461. static int hexval[] = {
  462.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  463.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  464.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  465.   0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
  466.   0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  467.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  468.   0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  469.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  470. };
  471.  
  472. /* Return a pointer to the first space-separated token past
  473. ** the character zLine[*pI].  Return NULL if there are no more
  474. ** tokens.  *pI is left pointing to the first character after
  475. ** the token.
  476. **
  477. ** The HTTP-style encoding is removed from the token and the
  478. ** token is null-terminated.
  479. */
  480. static char *GetToken(char *zLine, int *pI){
  481.   int i = *pI;
  482.   int iStart;
  483.   int j;
  484.   while( isspace(zLine[i]) ){ i++; }
  485.   if( zLine[i]==0 ) return 0;
  486.   iStart = i;
  487.   while( zLine[i] && !isspace(zLine[i]) ){ i++; }
  488.   if( zLine[i] ){
  489.     *pI = i + 1;
  490.     zLine[i] = 0;
  491.   }else{
  492.     *pI = i;
  493.   }
  494.   for(i=j=iStart; zLine[i]; i++){
  495.     switch( zLine[i] ){
  496.       case '+':
  497.        zLine[j++] = ' ';
  498.        break;
  499.       case '%':
  500.        if( zLine[i+1] && zLine[i+2] ){
  501.          zLine[j++] = hexval[0x7f & zLine[i+1]]*16 + hexval[0x7f & zLine[i+2]];
  502.          i += 2;
  503.        }else{
  504.          zLine[j++] = '%';
  505.        }
  506.        break;
  507.       default:
  508.        zLine[j++] = zLine[i];
  509.        break;
  510.     }
  511.   }
  512.   zLine[j] = 0;
  513.   return &zLine[iStart];
  514. }
  515.  
  516. /*
  517. ** This function inserts a new entry into the database (or replaces
  518. ** the value of an existing entry).  iRec is the record number.
  519. ** New records are created as necessary.  zField is the entry label,
  520. ** and zValue is its value.  Both are plain text.
  521. **
  522. ** If zValue==0 or *zValue==0, then the entry is deleted.
  523. **
  524. ** Memory to hold the new entry is obtained from malloc.
  525. */
  526. static void InsertEntry(int iRec, char *zLabel, char *zValue){
  527.   ConfEntry *p;
  528.   if( zValue==0 || *zValue==0 ){
  529.     if( iRec>=0 && iRec<nRecord ){
  530.       ConfEntry **pp = &aRecord[iRec];
  531.       while( (p=*pp)!=0 ){
  532.         if( strcmp(p->zLabel,zLabel)==0 ) break;
  533.         pp = &p->next;
  534.       }
  535.       if( p ){
  536.         *pp = p->next;
  537.         free( p->zValue );
  538.         free( p );
  539.       }
  540.     }
  541.     return;
  542.   }
  543.   if( iRec>=nRecord ){
  544.     if( nRecord==0 ){
  545.       aRecord = malloc( sizeof(ConfEntry*)*(iRec+1) );
  546.     }else{
  547.       aRecord = realloc( aRecord, sizeof(ConfEntry*)*(iRec+1) );
  548.     }
  549.     if( aRecord==0 ){ nRecord = 0; return; }
  550.     while( nRecord<=iRec ){
  551.       aRecord[nRecord++] = 0;
  552.     }
  553.   }
  554.   for(p=aRecord[iRec]; p; p=p->next){
  555.     if( strcmp(p->zLabel,zLabel)==0 ) break;
  556.   }
  557.   if( p==0 ){
  558.     p = malloc( sizeof(ConfEntry) + strlen(zLabel) + 1 );
  559.     if( p==0 ) return;
  560.     p->zLabel = (char*)&p[1];
  561.     strcpy(p->zLabel, zLabel);
  562.     p->next = aRecord[iRec];
  563.     p->zValue = 0;
  564.     aRecord[iRec] = p;
  565.   }
  566.   if( p ){
  567.     if( p->zValue ) free(p->zValue);
  568.     p->zValue = malloc( strlen(zValue) + 1 );
  569.     if( p->zValue ) strcpy(p->zValue, zValue);
  570.   }
  571. }
  572.  
  573. /*
  574. ** The set of characters that need to be excaped are coded as "1".
  575. ** Safe characters are 0.
  576. */
  577. static unsigned char NeedEsc[] = {
  578.   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  579.   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  580.   1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0,
  581.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  582.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  583.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
  584.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  585.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1,
  586.   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  587.   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  588.   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  589.   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  590.   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  591.   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  592.   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  593.   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  594. };
  595.  
  596. /* Do an HTTP-style encoding of zToken and write it on the
  597. ** output file given.
  598. */
  599. static void WriteToken(FILE *out, char *zToken){
  600.   int i = 0;
  601.   while( zToken[i] ){
  602.     if( !NeedEsc[(unsigned)zToken[i]&0xff] ){
  603.       i++;
  604.     }else{
  605.       if( i>0 ){
  606.         fwrite(zToken,1,i,out);
  607.       }
  608.       if( zToken[i]==' ' ){
  609.         fwrite("+",1,1,out);
  610.       }else{
  611.         char zBuf[4];
  612.         zBuf[0] = '%';
  613.         zBuf[1] = "0123456789ABCDEF"[(zToken[i]>>4)&0xf];
  614.         zBuf[2] = "0123456789ABCDEF"[zToken[i]&0xf];
  615.         zBuf[3] = 0;
  616.         fwrite(zBuf,1,3,out);
  617.       }
  618.       zToken += i + 1;
  619.       i = 0;
  620.     }
  621.   }
  622.   if( i>0 ){
  623.     fwrite(zToken,1,i,out);
  624.   }
  625. }
  626.  
  627. /*
  628. ** The set of characters that need to be excaped like "\012" are
  629. ** are coded as 1.  Safe characters are 0.  Characters that need
  630. ** a backslash in front are coded with 2.  Another code generates
  631. ** a symbolic escape by putting a backslash in front of the value.
  632. */
  633. static unsigned char NeedTclEsc[] = {
  634.   1,  1,  1,  1,  1,  1,  1, 'a',  'b','t','n', 1, 'f','r', 1,  1,
  635.   1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  636.   2,  0,  2,  1,  2,  0,  0,  0,    0,  0,  0,  0,  0,  0,  0,  0,
  637.   0,  0,  0,  0,  0,  0,  0,  0,    0,  0,  0,  0,  0,  0,  0,  0,    
  638.   0,  0,  0,  0,  0,  0,  0,  0,    0,  0,  0,  0,  0,  0,  0,  0,    
  639.   0,  0,  0,  0,  0,  0,  0,  0,    0,  0,  0,  2,  2,  2,  0,  0,    
  640.   0,  0,  0,  0,  0,  0,  0,  0,    0,  0,  0,  0,  0,  0,  0,  0,    
  641.   0,  0,  0,  0,  0,  0,  0,  0,    0,  0,  0,  1,  0,  1,  0,  1,    
  642.   1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  643.   1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  644.   1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  645.   1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  646.   1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  647.   1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  648.   1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  649.   1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  650. };
  651.  
  652. /* Write a string as a single TCL token.
  653. */
  654. static void WriteTcl(FILE *out, char *z){
  655.   register c;
  656.   while( (c=*(z++))!=0 ){
  657.     if( NeedTclEsc[c] ){
  658.       int e = NeedTclEsc[c];
  659.       if( e==1 ){
  660.         fprintf(out,"\\%03o",0xff&c);
  661.       }else if( e==2 ){
  662.         fprintf(out,"\\%c",c);
  663.       }else{
  664.         fprintf(out,"\\%c",e);
  665.       }
  666.     }else{
  667.       putc(c,out);
  668.     }
  669.   }
  670. }
  671.  
  672. /*
  673. ** The set of characters that need to be excaped like "\012" are
  674. ** are coded as 1.  Safe characters are 0.  Characters that need
  675. ** a backslash in front are coded with 2.  Another code generates
  676. ** a symbolic escape by putting a backslash in front of the value.
  677. */
  678. static unsigned char NeedHumanEsc[] = {
  679.   1,  1,  1,  1,  1,  1,  1, 'a',  'b','t','n', 1, 'f','r', 1,  1,
  680.   1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  681.   0,  0,  2,  0,  2,  0,  0,  0,    0,  0,  0,  0,  0,  0,  0,  0,
  682.   0,  0,  0,  0,  0,  0,  0,  0,    0,  0,  0,  0,  0,  0,  0,  0,    
  683.   0,  0,  0,  0,  0,  0,  0,  0,    0,  0,  0,  0,  0,  0,  0,  0,    
  684.   0,  0,  0,  0,  0,  0,  0,  0,    0,  0,  0,  0,  2,  0,  0,  0,    
  685.   0,  0,  0,  0,  0,  0,  0,  0,    0,  0,  0,  0,  0,  0,  0,  0,    
  686.   0,  0,  0,  0,  0,  0,  0,  0,    0,  0,  0,  0,  0,  0,  0,  1,    
  687.   1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  688.   1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  689.   1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  690.   1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  691.   1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  692.   1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  693.   1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  694.   1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  695. };
  696.  
  697. /* Write a string as a single TCL token.
  698. */
  699. static void WriteHumanReadable(FILE *out, char *z){
  700.   register c;
  701.   while( (c=*(z++))!=0 ){
  702.     if( NeedHumanEsc[c] ){
  703.       int e = NeedHumanEsc[c];
  704.       if( e==1 ){
  705.         fprintf(out,"\\%03o",0xff&c);
  706.       }else if( e==2 ){
  707.         fprintf(out,"\\%c",c);
  708.       }else{
  709.         fprintf(out,"\\%c",e);
  710.       }
  711.     }else{
  712.       putc(c,out);
  713.     }
  714.   }
  715. }
  716.  
  717. /*
  718. ** Convert a non-negative number from its ASCII form into a binary
  719. ** form.  Return -1 if the input string is not a non-negative number.
  720. */
  721. static int GetInt(char *z){
  722.   int v = 0;
  723.   while( *z ){
  724.     if( *z<'0' || *z>'9' ) return -1;
  725.     v = v*10 + *(z++) - '0';
  726.   }
  727.   return v;
  728. }
  729.  
  730. /*
  731. ** Read the whole database into memory.
  732. */
  733. static void ReadDb(void){
  734.   char *zDb;
  735.   int i,j;
  736.   char *zLine;
  737.   char *zToken;
  738.   int iRec;
  739.   char *zField;
  740.   char *zValue;
  741.  
  742.   zDb = ReadFile(DB_FILE);
  743.   if( zDb==0 ) return;
  744.   i = 0;
  745.   while( (zLine=GetLine(zDb,&i))!=0 ){
  746.     if( *zLine=='#' ) continue;
  747.     j = 0;
  748.     zToken = GetToken(zLine,&j);
  749.     if( zToken==0 ) continue;
  750.     iRec = GetInt(zToken);
  751.     if( iRec<0 ) continue;
  752.     zField = GetToken(zLine,&j);
  753.     zValue = GetToken(zLine,&j);
  754.     if( zValue==0 ) continue;
  755.     InsertEntry(iRec,zField,zValue);
  756.   }
  757.   free( zDb );
  758. }
  759.  
  760. /* Given two lists of ConfEntry structures both of which are
  761. ** sorted and either or both of which can be empty, combine the
  762. ** two lists into a single sorted list.  Return a pointer to 
  763. ** the head of the new list.
  764. **
  765. ** This is part of the merge-sort algorithm.
  766. */
  767. static ConfEntry *merge(ConfEntry *left, ConfEntry *right){
  768.   ConfEntry *head = 0, *tail = 0;
  769.   while( left && right ){
  770.     int c = strcmp(left->zLabel, right->zLabel);
  771.     if( c<0 ){
  772.       if( tail ){
  773.         tail->next = left;
  774.         tail = tail->next;
  775.       }else{
  776.         head = tail = left;
  777.       }
  778.       left = left->next;
  779.       tail->next = 0;
  780.     }else{
  781.       if( tail ){
  782.         tail->next = right;
  783.         tail = tail->next;
  784.       }else{
  785.         head = tail = right;
  786.       }
  787.       right = right->next;
  788.       tail->next = 0;
  789.     }
  790.   }
  791.   if( left ){
  792.     if( tail ){
  793.       tail->next = left;
  794.     }else{
  795.       head = tail = left;
  796.     }
  797.   }else if( right ){
  798.     if( tail ){
  799.       tail->next = right;
  800.     }else{
  801.       head = tail = right;
  802.     }
  803.   }
  804.   return head;
  805. }
  806.  
  807. /* Sort a list of ConfEntry structures.  Return a pointer to
  808. ** the sorted list.
  809. **
  810. ** The algorithm is merge-sort.  
  811. */
  812. static ConfEntry *sort(ConfEntry *p){
  813.   int i;
  814.   ConfEntry *pNext;
  815.   ConfEntry *a[32];
  816.  
  817.   for(i=0; i<sizeof(a)/sizeof(a[0]); i++){ a[i] = 0; }
  818.   while( p ){
  819.     pNext = p->next;
  820.     p->next = 0;
  821.     for(i=0; a[i] && i<sizeof(a)/sizeof(a[0])-1; i++){
  822.       p = merge(a[i],p);
  823.       a[i] = 0;
  824.     }
  825.     a[i] = p;
  826.     p = pNext;
  827.   }
  828.   for(i=0; i<sizeof(a)/sizeof(a[0]); i++){
  829.     p = merge(p,a[i]);
  830.   }
  831.   return p;
  832. }
  833.  
  834. /*
  835. ** Write the entire database back to the disk.  Make sure the
  836. ** database file is owned by root and has 0600 permissions so
  837. ** that unprivileged users can't see the passwords.
  838. */
  839. static void WriteDb(void){
  840.   FILE *out;
  841.   int i;
  842.   ConfEntry *p;
  843.   int fd;
  844.   char zFilename[sizeof(DB_FILE)+100];
  845.  
  846.   sprintf(zFilename,"%s.%d",DB_FILE,getpid());
  847.   out = fopen(zFilename,"w");
  848.   if( out==0 ) return;
  849.   fd = fileno(out);
  850.   fchmod(fd,0600);
  851.   fchown(fd,0,0);
  852.   for(i=0; i<nRecord; i++){
  853.     aRecord[i] = p = sort(aRecord[i]);
  854.     for(p=aRecord[i]; p; p=p->next){
  855.       fprintf(out,"%d %s ",i,p->zLabel);
  856.       WriteToken(out,p->zValue);
  857.       fprintf(out,"\n");
  858.     }
  859.   } 
  860.   fclose(out);
  861.   unlink(DB_FILE);
  862.   link(zFilename,DB_FILE);
  863.   unlink(zFilename);
  864. }
  865.  
  866. /*
  867. ** Given a sequence of strings of the form
  868. **
  869. **              LABEL=VALUE
  870. **
  871. ** parse the strings up into LABEL and VALUE and add entries
  872. ** for each string to the database as record iRec.
  873. */
  874. static void AddEntries(int iRec, int argc, char **argv){
  875.   int i, j;
  876.   char *zLabel;
  877.   char *zValue;
  878.   for(i=0; i<argc; i++){
  879.     zLabel = argv[i];
  880.     for(j=1; zLabel[j] && zLabel[j]!='='; j++){}
  881.     if( zLabel[j]!='=' ) continue;
  882.     zLabel[j] = 0;
  883.     zValue = &zLabel[j+1];
  884.     InsertEntry(iRec, zLabel, zValue);
  885.   }
  886. }
  887.  
  888. /*
  889. ** Rearrange the order of records so that the Mth record
  890. ** becomes the Nth record.
  891. */
  892. static void MoveRecord(int M, int N){
  893.   int i;
  894.   ConfEntry *pTemp;
  895.  
  896.   if( nRecord<0 ) return;
  897.   if( M>=nRecord ) M = nRecord-1;
  898.   if( M<0 ) M = 0;
  899.   if( N>=nRecord ) N = nRecord-1;
  900.   if( N<0 ) N = 0;
  901.   if( M==N ) return;
  902.   if( M<N ){
  903.     pTemp = aRecord[M];
  904.     for(i=M; i<N; i++){
  905.       aRecord[i] = aRecord[i+1];
  906.     }
  907.     aRecord[N] = pTemp;
  908.   }else{
  909.     pTemp = aRecord[M];
  910.     for(i=M; i>N; i--){
  911.       aRecord[i] = aRecord[i-1];
  912.     }
  913.     aRecord[N] = pTemp;
  914.   }
  915. }
  916.  
  917. /*
  918. ** Return TRUE if the given string is an IP address composed of
  919. ** 4 numbers between 0 and 255 separated by dots.  If the string
  920. ** is an IP address, return the value in *pIp.
  921. */
  922. static int IsIpAddr(char *z,  int *pIp){
  923.   int a1, a2, a3, a4, rc;
  924.   if( sscanf(z,"%d.%d.%d.%d",&a1,&a2,&a3,&a4)==4
  925.     && a1>=0 && a1<=255
  926.     && a2>=0 && a2<=255
  927.     && a3>=0 && a3<=255
  928.     && a4>=0 && a4<=255
  929.   ){
  930.     *pIp = (a1<<24) | (a2<<16) | (a3<<8) | a4;
  931.     rc = 1;
  932.   }else{
  933.     rc = 0;
  934.   }
  935.   return rc;
  936. }
  937.  
  938. /*
  939. ** Look up a specific field in a specific record and return its
  940. ** value.  Return the given default value if it is not found.
  941. */
  942. static char *Lookup(int iRec, char *zLabel, char *zDflt){
  943.   ConfEntry *p;
  944.   if( iRec<0 || iRec>=nRecord ) return zDflt;
  945.   for(p=aRecord[iRec]; p; p=p->next){
  946.     if( strcmp(zLabel,p->zLabel)==0 ) return p->zValue;
  947.   }
  948.   return zDflt;
  949. }
  950.  
  951. /*
  952. ** Given a command-line parameter that might be a record number,
  953. ** or an IP address, or a modem tty name, or the keyword "all",
  954. ** return a record number.  The special code FIND_ERROR is returned
  955. ** if we can't find a match.  FIND_ALL is returned if the keyword
  956. ** "all" appears or for a NULL string.
  957. */
  958. #define FIND_ALL    -1
  959. #define FIND_ERROR  -2
  960. static int FindRecord(char *z){
  961.   int i;
  962.   int ip;
  963.  
  964.   if( z==0 ){
  965.     if( nRecord==1 ) return 0;
  966.     return FIND_ALL;
  967.   }
  968.   if( strcmp(z,"all")==0 ){
  969.     return FIND_ALL;
  970.   }
  971.   i = GetInt(z);
  972.   if( i>=0 ){
  973.     if( i>=nRecord ) i = nRecord-1;
  974.     if( i<0 ) i = 0;
  975.     return i;
  976.   }
  977.   if( IsIpAddr(z,&ip) ){
  978.     int t, mask, iRec;
  979.     char *zVal;
  980.     for(iRec=0; iRec<nRecord; iRec++){
  981.       zVal = Lookup(iRec,"ip","0.0.0.0");
  982.       if( !IsIpAddr(zVal,&t) ) continue;
  983.       zVal = Lookup(iRec,"netmask","0.0.0.0");
  984.       if( !IsIpAddr(zVal,&mask) ) continue;
  985.       if( (t&mask)==(ip&mask) ) return iRec;
  986.     }
  987.   }
  988.   if( z[0]=='/' ){
  989.     int iRec;
  990.     char *zVal;
  991.     for(iRec=0; iRec<nRecord; iRec++){
  992.       zVal = Lookup(iRec,"tty","/dev/modem");
  993.       if( strcmp(zVal,z)==0 ) return iRec;
  994.     }
  995.   }
  996.   for(i=0; i<nRecord; i++){
  997.     char *zVal = Lookup(i,"service",0);
  998.     if( zVal && strcmp(zVal,z)==0 ) return i;
  999.   }
  1000.   return FIND_ERROR;
  1001. }
  1002.  
  1003. /*
  1004. ** Write a timestamp on the transcript.
  1005. */
  1006. static void WriteTimestamp(void){
  1007.   struct timeval stv;
  1008.   struct timezone stz;
  1009.   if( transcript==0 ) return;
  1010.   gettimeofday(&stv,&stz);
  1011.   fprintf(transcript,"%02ld:%02ld.%03ld", (long)(stv.tv_sec/60)%60,
  1012.     (long)stv.tv_sec % 60, (long)stv.tv_usec/1000);
  1013. }
  1014.  
  1015. /*
  1016. ** Write to a file descriptor, and also to the transcript.
  1017. */
  1018. static void ChatWrite(char *z, int shroud){
  1019.   int n, w;
  1020.   if( transcript ){
  1021.     if( shroud>0 ){
  1022.       int i = strlen(z);
  1023.       while( i>0 ){
  1024.         fprintf(transcript,"%.*s",i,"**********" "**********" "***********");
  1025.         i -= 30;
  1026.       }
  1027.     }else{
  1028.       WriteHumanReadable(transcript,z);
  1029.     }
  1030.   }
  1031.   n = strlen(z);
  1032.   while( n ){
  1033.     w = write(1, z, n);
  1034.     if( w>0 ){
  1035.       n -= w;
  1036.       z += w;
  1037.     }
  1038.   }
  1039. }
  1040.  
  1041. /*
  1042. ** Send output to the modem. 
  1043. **
  1044. ** Delay for "delay" microseconds before actually doing the write.
  1045. ** This is for modems that get fouled up by input that follows to
  1046. ** closely after their own output.
  1047. **
  1048. ** The transmitted text is normally written to the transcript file.
  1049. ** But if "shroud" is one, then "*" characters are subsitituted.
  1050. ** This is used when writting the password, to keep it from view.
  1051. */
  1052. static void ChatSend(int delay, int shroud, char *z, ...){
  1053.   va_list ap;
  1054.   if( z==0 ) return;
  1055.   if( delay>0 ){
  1056.     usleep(delay);
  1057.   }
  1058.   if( transcript ){
  1059.     WriteTimestamp();
  1060.     fprintf(transcript," Send:  \"");
  1061.   }
  1062.   va_start(ap, z);
  1063.   ChatWrite(z, shroud);
  1064.   while( (z = va_arg(ap,char*))!=0 ){
  1065.     ChatWrite(z, shroud);
  1066.     shroud--;
  1067.   }
  1068.   if( transcript ){
  1069.     fprintf(transcript,"\"\n");
  1070.     fflush(transcript);
  1071.   }
  1072. }
  1073.  
  1074. /*
  1075. ** Read text from the input and put it into zBuf.  Reading continues
  1076. ** until:
  1077. **
  1078. **   *  No characters are received after waiting for "timeout" 
  1079. **      seconds, or
  1080. **
  1081. **   *  Some characters have been received but no characters have
  1082. **      appeared for 500 milliseconds or more.
  1083. **
  1084. ** Only characters since the last new-line are returned in zBuf.
  1085. ** But all received characters are written to the transcript. 
  1086. **
  1087. ** Characters in zBuf[] when this routine is called are not erased
  1088. ** or overwritten.  Newly read characters are appended.  Unless
  1089. ** a new-line or carriage routine is seen, when all characters are
  1090. ** deleted.
  1091. **
  1092. ** The function returns the number of characters that were received
  1093. ** during the current call to this function.  This number will be
  1094. ** zero if a timeout occurs.
  1095. */
  1096. static int ChatRecv(char *zBuf, int nBuf, int timeout){
  1097.   int n;            /* Number of bytes returned from a single read() */
  1098.   int i;            /* Loop counter */
  1099.   int idle = 0;     /* Number of 1/10ths of second since last read() */
  1100.   int got;          /* How many characters previously read */
  1101.   int nChar = 0;    /* Total number of characters read */
  1102.   
  1103.   time_t start;
  1104.   time_t now;
  1105.   
  1106.   /* Remember when we began looking... */
  1107.   time(&start);
  1108.  
  1109.   /* Figure out how much text is already in zBuf[].  New
  1110.   ** text will be appended.
  1111.   */
  1112.   for(got=0; zBuf[got]; got++){}
  1113.   if( got>0 && (zBuf[got-1]=='\n' || zBuf[got-1]=='\r') ){ got = 0; }
  1114.   zBuf[got] = 0;
  1115.  
  1116.   /* Start reading input...
  1117.   */
  1118.   while( got<nBuf-1 && (nChar==0 || idle<5) ){
  1119.     time(&now);
  1120.     if( start+timeout < now ) break;
  1121.     n = read(0, &zBuf[got], nBuf-1-got);
  1122.     if( n<0 ) n = 0;
  1123.     nChar += n;
  1124.     if( n==0 ){
  1125.       usleep(100000);
  1126.       idle++;
  1127.       continue;
  1128.     }
  1129.     zBuf[got+n] = 0;
  1130.     idle = 0;
  1131.     got += n;
  1132.  
  1133.     /* Remove any characters that are followed by a '\n'.  Except,
  1134.     ** don't remove characters if the '\n' is followed by nothing
  1135.     ** unless '\n' is on a line by itself or preceded by a single
  1136.     ** '\r'.
  1137.     */
  1138.     for(i=0; i<got; i++){
  1139.       if( zBuf[i]=='\n' && (zBuf[i+1]!=0 || i==0 || (i==1 && zBuf[0]=='\r')) ){
  1140.         if( transcript ){
  1141.           int c;
  1142.           WriteTimestamp();
  1143.           fprintf(transcript," Skip:  \"");
  1144.           c = zBuf[i+1];
  1145.           zBuf[i+1] = 0;
  1146.           WriteHumanReadable(transcript,zBuf);
  1147.           zBuf[i+1] = c;
  1148.           fprintf(transcript,"\"\n");
  1149.           fflush(transcript);
  1150.         }
  1151.         strcpy(zBuf, &zBuf[i+1]);
  1152.         got -= i+1;
  1153.         i = -1;
  1154.       }
  1155.     }
  1156.   }
  1157.   if( transcript && zBuf[0] ){
  1158.     WriteTimestamp();
  1159.     fprintf(transcript," Recv:  \"");
  1160.     WriteHumanReadable(transcript,zBuf);
  1161.     fprintf(transcript,"\"\n");
  1162.     fflush(transcript);
  1163.   }
  1164.   return nChar;
  1165. }
  1166.  
  1167. /*
  1168. ** Return true if zPattern occurs anywhere in zBuf
  1169. */
  1170. static int Match(char *zBuf, char *zPattern){
  1171.   int len = strlen(zPattern);
  1172.   while( *zBuf ){
  1173.     if( *zBuf==*zPattern && strncmp(zBuf,zPattern,len)==0 ){
  1174.       if( transcript ){
  1175.         WriteTimestamp();
  1176.         fprintf(transcript," Match: \"");
  1177.         WriteHumanReadable(transcript,zPattern);
  1178.         fprintf(transcript,"\"\n");
  1179.         fflush(transcript);
  1180.       }
  1181.       return 1;
  1182.     }
  1183.     zBuf++;
  1184.   }
  1185.   return 0;
  1186. }
  1187.  
  1188. /*
  1189. ** The following array maps 8-bit characters into 7-bit characters
  1190. ** and upper-case into lower-case.
  1191. */
  1192. static unsigned char SevenBit[] = {
  1193.    0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
  1194.   16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
  1195.   32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
  1196.   48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
  1197.   64, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
  1198.  112,113,114,115,116,117,118,119,120,121,122, 91, 92, 93, 94, 95,
  1199.   96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
  1200.  112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
  1201.  128,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
  1202.   16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
  1203.   32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
  1204.   48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
  1205.   64, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
  1206.  112,113,114,115,116,117,118,119,120,121,122, 91, 92, 93, 94, 95,
  1207.   96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
  1208.  112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
  1209. };
  1210.  
  1211. /*
  1212. ** Compare two strings.  In the first string, use only the lower
  1213. ** 7 bits and convert all upper case to lower case letters.
  1214. */
  1215. static int StrNCmp7(char *zBuf, char *zPattern, int n){
  1216.   while( n-- ){
  1217.     int a = SevenBit[(int)*((unsigned char*)zBuf++)];
  1218.     int b = (int)*((unsigned char*)zPattern++);
  1219.     if( a==0 || b==0 || a!=b ){ return a - b; }
  1220.   }
  1221.   return 0;
  1222. }
  1223.  
  1224. /*
  1225. ** Like Match(), but only uses the lower seven 7 bits of zBuf
  1226. ** and treats all characters in zBuf as lower-case.
  1227. */
  1228. static int Match7(char *zBuf, char *zPattern){
  1229.   int len = strlen(zPattern);
  1230.   while( *zBuf ){
  1231.     if( SevenBit[*(unsigned char*)zBuf]==*zPattern 
  1232.     && StrNCmp7(zBuf,zPattern,len)==0 ){
  1233.       if( transcript ){
  1234.         WriteTimestamp();
  1235.         fprintf(transcript," Match: \"");
  1236.         WriteHumanReadable(transcript,zPattern);
  1237.         fprintf(transcript,"\"\n");
  1238.         fflush(transcript);
  1239.       }
  1240.       return 1;
  1241.     }
  1242.     zBuf++;
  1243.   }
  1244.   return 0;
  1245. }
  1246.  
  1247. /*
  1248. ** Return TRUE if the input line looks like a prompt of some kind.
  1249. ** A "prompt" is a seqence of words followed by a colon, or perhaps
  1250. ** something like "->".
  1251. */
  1252. static int IsPrompt(char *zLine){
  1253.   int i;
  1254.   for(i=0; zLine[i]!=0 && zLine[i]!='\n' && zLine[i]!='\r'; i++){}
  1255.   while( i>0 && isspace(zLine[i-1]) ){ i--; }
  1256.   i--;
  1257.   if( i<=0 ) return 0;
  1258.   if( zLine[i]==':' ) return 1;
  1259.   if( zLine[i]=='?' ) return 1;
  1260.   if( zLine[i]=='>' ) return 1;
  1261.   if( zLine[i]=='$' ) return 1;
  1262.   return 0;
  1263. }
  1264.  
  1265. #define MAX_TIME      70   /* Maximum time to connect */
  1266. #define DIAL_TIMEOUT "60"  /* How long to wait for dialing to complete */
  1267. #define CHAT_TIMEOUT  "3"  /* How long to wait for responses after dialing */
  1268.  
  1269. /* This routine is pretty much the whole point.  Assuming a
  1270. ** modem is connected on file descriptors 0 and 1, initialize
  1271. ** the modem, dial the phone number, supply user ID and password
  1272. ** (if requested).  Return successfully (0) if we see the start of
  1273. ** PPP communication and return an error (1) if anything goes
  1274. ** wrong.
  1275. **
  1276. ** This routine runs as root so that it will have permission
  1277. ** to open the output file associated with WriteChatStat().
  1278. */
  1279. static int Chat(int iRec){
  1280.   struct termios tio_0_saved;
  1281.   struct termios tio_1_saved;
  1282.   struct termios tio_new;
  1283.   int flags;
  1284.   int i;
  1285.   char *zVal;
  1286.   int timeout;
  1287.   int dial_timeout;
  1288.   int nTimeout = 0;
  1289.   time_t start, now;
  1290.   int rc = 4;
  1291.   int chatDelay = 0;
  1292.   char *zChat;
  1293.   char *zPhone;
  1294.   char *zExpect[10];
  1295.   char *zReply[10];
  1296.   char zLine[1000];
  1297.  
  1298.   tcgetattr(0,&tio_0_saved);
  1299.   tcgetattr(1,&tio_1_saved);
  1300.   tio_new = tio_0_saved;
  1301.   cfmakeraw(&tio_new);
  1302.   tcsetattr(0,TCSANOW,&tio_new);
  1303.   tio_new = tio_1_saved;
  1304.   cfmakeraw(&tio_new);
  1305.   tcsetattr(1,TCSANOW,&tio_new);
  1306.   flags = fcntl(0, F_GETFL);
  1307.   fcntl(0, F_SETFL, O_NONBLOCK);
  1308.  
  1309.   sprintf(zLine,"%s/transcript.%d",DB_DIR,iRec);
  1310.   transcript = fopen(zLine,"a");
  1311.   if( transcript ){
  1312.     fchmod(fileno(transcript),0644);
  1313.     fchown(fileno(transcript),0,0);
  1314.   }
  1315.  
  1316.   zChat = Lookup(iRec,"chat","yes");
  1317.   chatDelay = atoi(Lookup(iRec,"delay","0"))*1000;
  1318.   for(i=0; i<=9; i++){
  1319.     static char *dfltInit[10] = { "atz", "at&d3", };
  1320.     sprintf(zLine,"init%d",i);
  1321.     zVal = Lookup(iRec,zLine,dfltInit[i]);
  1322.     if( zVal ){
  1323.       ChatSend(chatDelay,0,zVal,"\r",0);
  1324.       zLine[0] = 0;
  1325.       ChatRecv(zLine,sizeof(zLine),1000);
  1326.       if( !Match(zLine,"OK") ){
  1327.         ChatRecv(zLine,sizeof(zLine),1000);
  1328.       }
  1329.     }
  1330.     sprintf(zLine,"expect%d",i);
  1331.     zExpect[i] = Lookup(iRec,zLine,0);
  1332.     sprintf(zLine,"reply%d",i);
  1333.     zReply[i] = Lookup(iRec,zLine,0);
  1334.   }
  1335.   zPhone = Lookup(iRec,"phone",0);
  1336.   if( zPhone==0 ){
  1337.     WriteChatStat(iRec,"No phone number specified");
  1338.     return 1;
  1339.   }
  1340.   ChatSend(chatDelay,0,"atd", zPhone, "\r", 0);
  1341.   timeout = dial_timeout = atoi(Lookup(iRec,"dialtimeout",DIAL_TIMEOUT));
  1342.   time(&start);
  1343.   zLine[0] = 0;
  1344.   nTimeout = 0;
  1345.   while( 1 ){
  1346.     int nChar = ChatRecv(zLine,sizeof(zLine),timeout);
  1347.     time(&now);
  1348.     if( now-start > MAX_TIME ){
  1349.       WriteChatStat(iRec,"Login timeout at");
  1350.       break;
  1351.     }
  1352.     if( zLine[0]=='a' && strncmp(zLine,"atd",3)==0
  1353.     && zLine[3]==zPhone[0] && strncmp(&zLine[3],zPhone,strlen(zPhone))==0 ){
  1354.       /* Skip the echo of the dial command to the modem */
  1355.       zLine[0] = 0;
  1356.       continue;
  1357.     }
  1358.     if( nChar==0 ){
  1359.       nTimeout++;
  1360.       if( timeout==dial_timeout ){
  1361.         WriteChatStat(iRec,"Modem could not connect at");
  1362.         break;
  1363.       }else if( (nTimeout>2 && *Lookup(iRec,"autostart","yes")!='n')
  1364.                 || zChat[0]=='n' ){
  1365.         rc = 0;
  1366.         break;
  1367.       }else{
  1368.         ChatSend(chatDelay,0,"\r",0);
  1369.         continue;
  1370.       }
  1371.     }
  1372.     timeout = atoi(Lookup(iRec,"chattimeout",CHAT_TIMEOUT));
  1373.     nTimeout = 0;
  1374.     for(i=0; i<=9; i++){
  1375.       if( zExpect[i]==0 || zReply[i]==0 ) continue;
  1376.       if( Match7(zLine,zExpect[i]) ){
  1377.         zLine[0] = 0;
  1378.         if( strcmp(zReply[i],"ACCEPT")==0 ){
  1379.           rc = 0;
  1380.           goto chat_done;
  1381.         }else if( strcmp(zReply[i],"FAIL")==0 ){
  1382.           goto chat_done;
  1383.         }else{
  1384.           ChatSend(chatDelay,0,zReply[i],"\r",0);
  1385.           nTimeout = 0;
  1386.           timeout = 3;
  1387.           break;
  1388.         }
  1389.       }
  1390.     }
  1391.     if( Match(zLine,"BUSY") ){
  1392.       WriteChatStat(iRec,"Busy signal at");
  1393.       break;
  1394.     }else if( Match(zLine,"NO CARRIER") ){
  1395.       WriteChatStat(iRec,"No carrier at");
  1396.       break;
  1397.     }else if( Match(zLine,"NO DIALTONE") ){
  1398.       WriteChatStat(iRec,"No dialtone at");
  1399.       break;
  1400.     }else if( Match(zLine,"NO ANSWER") ){
  1401.       WriteChatStat(iRec,"Ringing but no answer at");
  1402.       break;
  1403.     }else if( Match7(zLine,"ogin:") || Match7(zLine,"rname:") 
  1404.               || Match7(zLine,"-on:") || Match7(zLine,"serid:") ){
  1405.       zVal = Lookup(iRec,"user","anonymous");
  1406.       ChatSend(chatDelay,0,zVal,"\r",0);
  1407.       zLine[0] = 0;
  1408.     }else if( Match7(zLine,"ssword:") ){
  1409.       zVal = Lookup(iRec,"password","");
  1410.       ChatSend(chatDelay,1,zVal,"\r",0);
  1411.       zLine[0] = 0;
  1412.     }else if( Match(zLine,"~\377}#") || Match(zLine,"\300!}!") ){
  1413.       rc = 0;
  1414.       break;
  1415.     }else if( Match(zLine,"CONNECT") && zChat[0]=='n' ){
  1416.       rc = 0;
  1417.       break;
  1418. #if 0
  1419.     }else if( Match7(zLine,"destination:") || Match7(zLine,"response:") ){
  1420.       ChatSend(chatDelay,0,"ppp\r",0);
  1421.       zLine[0] = 0;
  1422. #endif
  1423.     }else if( IsPrompt(zLine) ){
  1424.       ChatSend(chatDelay,0,"ppp\r",0);
  1425.       zLine[0] = 0;
  1426.     }else{
  1427.       /* Ignore it */
  1428.     }
  1429.   }
  1430.  
  1431. chat_done:
  1432.   if( transcript ){
  1433.     WriteTimestamp();
  1434.     fprintf(transcript, rc ? " Fail\n" : " Accept\n");
  1435.     fclose(transcript);
  1436.   }
  1437.   fcntl(0,F_SETFL,flags);
  1438.   tcsetattr(0,TCSANOW,&tio_0_saved);
  1439.   tcsetattr(1,TCSANOW,&tio_1_saved);
  1440.   return rc;
  1441. }
  1442.  
  1443. /* Print status information for a given service.
  1444. */
  1445. static void PrintStatus(int iRec){
  1446.   char *zService;
  1447.   char *zStatus;
  1448.   char *zDiald;
  1449.   int pidDiald;
  1450.   char zBuf[30];
  1451.   char zFile[sizeof(DB_DIR)+100];
  1452.  
  1453.   if( iRec<0 || iRec>=nRecord ) return;
  1454.   sprintf(zFile,"%s/diald.%d",DB_DIR,iRec);
  1455.   zDiald = ReadFile(zFile);
  1456.   if( zDiald==0 || (pidDiald=atoi(zDiald))<=0 
  1457.      || kill(pidDiald,0)!=0 ){
  1458.     zDiald = "";
  1459.   }else{
  1460.     zDiald = "(diald running) ";
  1461.   }
  1462.   sprintf(zBuf,"service #%d",iRec);
  1463.   zService = Lookup(iRec,"service",zBuf);
  1464.   sprintf(zFile,"%s/status.%d",DB_DIR,iRec);
  1465.   zStatus = ReadFile(zFile);
  1466.   if( zStatus==0 ){
  1467.     printf("%s: %sStatus Unknown\n",zService, zDiald);
  1468.   }else{
  1469.     printf("%s: %s%s",zService, zDiald, zStatus);
  1470.     free( zStatus );
  1471.   }
  1472. }
  1473.  
  1474. /*
  1475. ** Return true if the given string contains a space or $ character
  1476. ** and therefore needs to be quoted for the shell.
  1477. */
  1478. static int NeedQuote(char *z){
  1479.   while( *z ){
  1480.     if( *z==' ' || *z=='$' || *z=='"' ) return 1;
  1481.     z++;
  1482.   }
  1483.   return 0;
  1484. }
  1485.  
  1486. /*
  1487. ** Open the transcript for record N and write the given argv[] string
  1488. ** to it.
  1489. */
  1490. static void WriteArgvToTranscript(int iRec, char **argv){
  1491.   int size;
  1492.   int i;
  1493.   char *zStr, *z;
  1494.   char zFile[sizeof(DB_DIR)+20];
  1495.  
  1496.   size = 0;
  1497.   for(i=0; argv[i]; i++){
  1498.     size += strlen(argv[i]) + 3;
  1499.   }
  1500.   zStr = z = malloc( size );
  1501.   if( zStr ){
  1502.     for(i=0; argv[i]; i++){
  1503.       if( NeedQuote(argv[i]) ){
  1504.         *z++ = '\'';
  1505.         strcpy(z,argv[i]);
  1506.         z += strlen(z);
  1507.         *z++ = '\'';
  1508.       }else{
  1509.         strcpy(z,argv[i]);
  1510.         z += strlen(z);
  1511.       }
  1512.       if( argv[i+1] ){
  1513.         *z++ = ' ';
  1514.       }
  1515.     }
  1516.     *z = 0;
  1517.     sprintf(zFile,"%s/transcript.%d", DB_DIR, iRec);
  1518.     WriteToFile(zFile, 0600, "%s\n", zStr);
  1519.   }
  1520. }
  1521.  
  1522. /* Check to see if diald is already running.  Return a process ID
  1523. ** for diald if it is running.  Or return 0 if there is no diald
  1524. ** running.
  1525. */
  1526. static int DialdPid(int iRec){
  1527.   char *zDiald;
  1528.   int pidDiald;
  1529.   char zDialdFile[sizeof(DB_DIR)+100];
  1530.  
  1531.   sprintf(zDialdFile, "%s/diald.%d", DB_DIR, iRec);
  1532.   zDiald = ReadFile(zDialdFile);
  1533.   if( zDiald==0 ) return 0;
  1534.   pidDiald = atoi(zDiald);
  1535.   free(zDiald);
  1536.   if( pidDiald==0 ){
  1537.     unlink(zDialdFile);
  1538.     return 0;
  1539.   }
  1540.   if( kill(pidDiald,0) ){
  1541.     unlink(zDialdFile);
  1542.     return 0;
  1543.   }
  1544.   return pidDiald;
  1545. }
  1546.  
  1547. /*
  1548. ** Shutdown the diald process
  1549. */
  1550. static int StopDiald(int iRec){
  1551.   int pidDiald;
  1552.  
  1553.   if( (pidDiald = DialdPid(iRec))!=0 ){
  1554.     kill(pidDiald,SIGTERM);
  1555.   }
  1556.   return 0;
  1557. }
  1558.  
  1559. /* Attempt to hang up the given port.
  1560. */
  1561. static void Hangup(int iRec){
  1562.   char *zDev;
  1563.   char *zPpp;
  1564.   int pidPpp = -1;
  1565.   int pidDiald = -1;
  1566.   int fd;
  1567.   int tries;
  1568.   struct termios ios;
  1569.   char zFile[sizeof(DB_DIR)+20];
  1570.  
  1571.   WriteStatus(iRec,"hangup at");
  1572.  
  1573.   /* Send a SIGINT to any diald process that is running.
  1574.   ** This is suppose to make diald hangup the line.
  1575.   */
  1576.   pidDiald = DialdPid(iRec);
  1577.   if( pidDiald ){
  1578.     kill(pidDiald,SIGINT);
  1579.     return;
  1580.   }
  1581.  
  1582.   /* We only reach this point if diald is not running.
  1583.   ** Send a SIGTERM to any running ppp process.  This is suppose
  1584.   ** to make ppp hang up the line and exit
  1585.   */
  1586.   sprintf(zFile,"%s/ppp.%d", DB_DIR, iRec);
  1587.   zPpp = ReadFile(zFile);
  1588.   if( zPpp && (pidPpp=atoi(zPpp))>0 ){
  1589.     kill(pidPpp,SIGTERM);
  1590.     sleep(1);
  1591.   }
  1592.  
  1593.   /* Try to open the TTY.  Try as many as three times waiting for
  1594.   ** one second between each attempt.  After the second failed attempt,
  1595.   ** send a SIGKILL to any diald or ppp process out there.
  1596.   */
  1597.   zDev = Lookup(iRec,"tty","/dev/modem");
  1598.   tries = 0;
  1599.   while( (fd=open(zDev,O_RDWR|O_NONBLOCK))<0 && tries<2 ){
  1600.     if( tries==1 ){
  1601.       if( pidPpp>0 ){
  1602.         kill(pidPpp, SIGKILL);
  1603.       }else if( pidDiald>0 ){
  1604.         kill(pidDiald, SIGKILL);
  1605.       }
  1606.     }
  1607.     tries++;
  1608.     sleep(1);
  1609.   }
  1610.  
  1611.   /* Change the buad rate of the TTY to 0.  This is suppose to make
  1612.   ** the DTR line go low, which should make the modem hangup.
  1613.   */ 
  1614.   if( fd>=0 ){
  1615.     tcgetattr(fd, &ios);
  1616.     cfsetospeed(&ios, B0);
  1617.     tcsetattr(fd, TCSANOW, &ios);
  1618.     close(fd);
  1619.   }
  1620. }
  1621.  
  1622. /*
  1623. ** Write into the given buffer the name of a file that will contain
  1624. ** the PID of the PPP process that is calling on serial device zDev.
  1625. ** Assume the size of the buffer into which we write is at least
  1626. ** sizeof(DB_DIR)+100.
  1627. **
  1628. ** The zDev probably contains '/' characters.  These must be converted
  1629. ** to something different.
  1630. */
  1631. static void MakeDeviceFile(char *zFile, char *zDev){
  1632.   int i;
  1633.   sprintf(zFile,"%s/device%.80s", DB_DIR, zDev);
  1634.   i = strlen(DB_DIR) + 5;
  1635.   while( zFile[i] ){
  1636.     if( zFile[i]=='/' ){ zFile[i] = '.'; }
  1637.     i++;
  1638.   }
  1639. }
  1640.  
  1641. /*
  1642. ** Given a modem device name, find the service number.
  1643. */
  1644. static int DeviceToRec(char *zDev){
  1645.   char *z;
  1646.   int iRec;
  1647.   char zFile[sizeof(DB_DIR)+100];
  1648.  
  1649.   MakeDeviceFile(zFile, zDev);
  1650.   z = ReadFile(zFile);
  1651.   if( z==0 || (iRec=atoi(z))<0 || iRec>=nRecord ){
  1652.     for(iRec=0; iRec<nRecord; iRec++){
  1653.       z = Lookup(iRec,"tty","/dev/modem");
  1654.       if( strcmp(z,zDev)==0 ) break;
  1655.     }
  1656.     if( iRec>nRecord ) iRec = FIND_ERROR;
  1657.   }
  1658.   return iRec;
  1659. }
  1660.  
  1661. /* This routine is called when "pppd" executes "ip-up".
  1662. */
  1663. static void IpUp(char **argv){
  1664.   int iRec;
  1665.   int i;
  1666.   char zFile[sizeof(DB_DIR)+20];
  1667.  
  1668.   iRec = DeviceToRec(argv[1]);
  1669.   if( iRec<0 ) return;
  1670.   WriteStatus(iRec,"%s %s to %s up since",argv[0],argv[3],argv[4]);
  1671.   sprintf(zFile,"%s/pid.%d", DB_DIR, iRec);
  1672.   unlink(zFile);
  1673.   for(i=0; i<=9; i++){
  1674.     char *zNet;
  1675.     char zRoute[1000];
  1676.     char zLabel[20];
  1677.     sprintf(zLabel,"route%d",i);
  1678.     zNet = Lookup(iRec,zLabel,0);
  1679.     if( zNet==0 ) continue;
  1680.     sprintf(zRoute,"/sbin/route add -net %.100s %.100s", zNet, argv[0]);
  1681.     system(zRoute);
  1682.   }
  1683. }
  1684.  
  1685. /* This routine is called when "pppd" executes "ip-down".
  1686. */
  1687. static void IpDown(char **argv){
  1688.   int iRec;
  1689.   char zFile[sizeof(DB_DIR)+80];
  1690.  
  1691.   iRec = DeviceToRec(argv[1]);
  1692.   if( iRec<0 ) return;
  1693.   WriteStatus(iRec,"down since");
  1694.   MakeDeviceFile(zFile, argv[1]);
  1695.   unlink(zFile);
  1696.   sprintf(zFile,"%s/ppp.%d", DB_DIR, iRec);
  1697.   unlink(zFile);
  1698. }
  1699.  
  1700. /*
  1701. ** This routine creates a session log for the most recent login
  1702. ** attempt.  The session log consists of two data sources:
  1703. **
  1704. **    1.  The modem dialing and logon transcript.
  1705. **
  1706. **    2.  All messages written to the /var/log/messages file by pppd.
  1707. **
  1708. ** The session log is overwritten after every logon attempt.
  1709. */
  1710. static void MakeSessionLog(time_t startTime, int pid, int iRec){
  1711.   FILE *out;
  1712.   char *z;
  1713.   struct tm *pTm;
  1714.   char zFile[sizeof(DB_DIR)+100];
  1715.   char zCmd[sizeof(DB_DIR)+100];
  1716.  
  1717.   out = fopen(SESSIONLOG,"w");
  1718.   if( out==0 ) return;
  1719.   fchmod(fileno(out),0644);
  1720.   fprintf(out,"This is a diagnostic log of the most recent login\n"
  1721.      "attempt by eznet.\n\n");
  1722.   z = Lookup(iRec,"service",0);
  1723.   if( z ){
  1724.     fprintf(out, "  ISP Name: \"%s\"\n", z);
  1725.   }
  1726.   pTm = localtime(&startTime);
  1727.   fprintf(out,"  Call Initiated: %s\n", asctime(pTm));
  1728.   sprintf(zFile,"%s/transcript.%d", DB_DIR, iRec);
  1729.   z = ReadFile(zFile);
  1730.   if( z ){
  1731.     int i;
  1732.     for(i=0; z[i] && z[i]!='\n'; i++){}
  1733.     fprintf(out,
  1734.       "The UNIX command used to initiate the connection attempt was: \n\n"
  1735.       "%.*s\n\n",
  1736.       i, z);
  1737.     if( z[i] ){ i++; }
  1738.     fprintf(out,"The interaction with the modem was as follows:\n\n"
  1739.       "%s\n", &z[i]);
  1740.     free(z);
  1741.   }else{
  1742.     fprintf(out,"The modem transcript could not be located!\n\n");
  1743.   }
  1744.   fprintf(out,
  1745.     "The following messages were written to the system log\n"
  1746.     "(in /var/log/messages) by pppd as it attempted to negotiate\n"
  1747.     "a PPP connection.");
  1748.   z = Lookup(iRec,"debug","n");
  1749.   if( z && *z=='y' ){
  1750.     fprintf(out,"  Details may be omitted by clearing the\n"
  1751.        "\"debug=%s\" property.\n\n", z);
  1752.   }else{
  1753.     fprintf(out,"  To see more detail, set the \"debug=yes\"\n"
  1754.        "property and reattempt the logon.\n\n");
  1755.   }
  1756.   fclose(out);
  1757.   sprintf(zCmd,"tail -200 /var/log/messages | grep 'pppd.%d.' >>%s",
  1758.     pid, SESSIONLOG);
  1759.   system(zCmd);
  1760. }
  1761.  
  1762. /* This routine is called in order to attempt to establish
  1763. ** a connection.  Return 0 if successful and non-zero if we fail.
  1764. */
  1765. static int Dial(int iRec){
  1766.   char *zPw;
  1767.   char *zDevice;
  1768.   char *zUser;
  1769.   char *zPpp;
  1770.   char *zService;
  1771.   char *zIdle;
  1772.   int pidPpp;
  1773.   int pidDiald;
  1774.   int rc;
  1775.   int isup = 1;
  1776.   time_t startTime;
  1777.   char zDeviceFile[sizeof(DB_DIR)+100];
  1778.   char zChatStat[sizeof(DB_DIR)+100];
  1779.   char zPidFile[sizeof(DB_DIR)+100];
  1780.   char zConnect[200];
  1781.   char zServiceBuf[50];
  1782.  
  1783.   /* Check to see if we are already running directly.
  1784.   */
  1785.   startTime = time(0);
  1786.   sprintf(zPidFile, "%s/pid.%d", DB_DIR, iRec);
  1787.   zPpp = ReadFile(zPidFile);
  1788.   if( zPpp && (pidPpp=atoi(zPpp))>0 && kill(pidPpp,0)==0 ){
  1789.     return 0; 
  1790.   }
  1791.  
  1792.   /* Check to see if diald is running.  If it is, then
  1793.   ** just send it a SIGUSR1 to bring the link up.
  1794.   */
  1795.   if( (pidDiald = DialdPid(iRec))>0 ){
  1796.     kill(pidDiald,SIGUSR1);
  1797.     return 0;
  1798.   }
  1799.  
  1800.   /* Initialize the ipup, ipdown, chap-secrets and pap-secrets files.
  1801.   */
  1802.   SetIpUpDown("up");
  1803.   SetIpUpDown("down");
  1804.   sprintf(zServiceBuf,"x%d",iRec);
  1805.   zService = Lookup(iRec,"service",zServiceBuf);
  1806.   zUser = Lookup(iRec,"user","anonymous");
  1807.   zPw = Lookup(iRec, "password", "*");
  1808.   if( zPw ){
  1809.     SetSecrets("pap-secrets",zService,zUser,zPw);
  1810.     SetSecrets("chap-secrets",zService,zUser,zPw);
  1811.   }
  1812.   zDevice = Lookup(iRec, "tty", "/dev/modem");
  1813.  
  1814.   /* Set the device file to the current ordinal number.  This is needed
  1815.   ** by the ipup and ipdown scripts.
  1816.   */
  1817.   MakeDeviceFile(zDeviceFile, zDevice);
  1818.   WriteToFile(zDeviceFile, 0644, "%d\n", iRec);
  1819.   WriteToFile(zPidFile, 0644, "%d\n", getpid());
  1820.  
  1821.   /* Set the status to dialing.
  1822.   */
  1823.   WriteStatus(iRec, "dialing since");
  1824.  
  1825.   /* The child does the work.  The parent waits for it to finish.
  1826.   */
  1827.   rc = fork();
  1828.   if( rc<0 ){
  1829.     WriteStatus(iRec,"fork failed");
  1830.     return 1;
  1831.   }
  1832.   if( rc==0 ){
  1833.     /* The child process */
  1834.     int argc = 0;
  1835.     char *z;
  1836.     int i;
  1837.     char zLabel[100];
  1838.     char *argv[200];
  1839.  
  1840.     argv[argc++] = Lookup(iRec, "pppd", PPP_EXE);
  1841.     argv[argc++] = zDevice;
  1842.     argv[argc++] = Lookup(iRec,"baud","115200");
  1843.     argv[argc++] = "connect";
  1844.     sprintf(zConnect,SELF " chat %d",iRec);
  1845.     argv[argc++] = zConnect;
  1846.     z = Lookup(iRec, "defaultroute", "y");
  1847.     if( *z=='y' ){
  1848.       argv[argc++] = "defaultroute";
  1849.     }
  1850.     argv[argc++] = "lock";
  1851.     argv[argc++] = "modem";
  1852.     argv[argc++] = "mtu";
  1853.     argv[argc++] = Lookup(iRec,"mtu","552");
  1854.     argv[argc++] = "mru";
  1855.     argv[argc++] = Lookup(iRec,"mru","552");
  1856.     z = Lookup(iRec,"debug",0);
  1857.     if( z && *z=='y' ){
  1858.       argv[argc++] = "debug";
  1859.     }
  1860.     argv[argc++] = "crtscts";
  1861.     argv[argc++] = "-detach";
  1862.     z = Lookup(iRec,"persist","no");
  1863.     if( *z=='y' ){
  1864.       argv[argc++] = "persist";
  1865.     }else{
  1866.       zIdle = Lookup(iRec,"idle","300");
  1867.       if( atoi(zIdle)>0 ){
  1868.         z = Lookup(iRec,"pppversion",DFLT_PPP_VERSION);
  1869.         argv[argc++] = strcmp(z,"2.3")<0 ? "idle-disconnect" : "idle";
  1870.         argv[argc++] = zIdle;
  1871.       }
  1872.     }
  1873.     argv[argc++] = "user";
  1874.     argv[argc++] = zUser;
  1875.     argv[argc++] = "remotename";
  1876.     argv[argc++] = zService;
  1877.     for(i=0; i<10; i++){
  1878.       sprintf(zLabel,"pppopt%d",i);
  1879.       z = Lookup(iRec,zLabel,0);
  1880.       if( z==0 ) break;
  1881.       argv[argc++] = z;
  1882.     }
  1883.     argv[argc] = 0;
  1884.     WriteArgvToTranscript(iRec,argv);
  1885.     execv(argv[0],argv);
  1886.     exit(0);
  1887.   }else{ 
  1888.     /* The parent process */
  1889.     char zPppFile[sizeof(DB_DIR)+20];
  1890.  
  1891.     sprintf(zPppFile,"%s/ppp.%d", DB_DIR, iRec);
  1892.     WriteToFile(zPppFile,0644,"%d\n",rc);
  1893.     while( access(zPidFile,0)==0 ){
  1894.       int status;
  1895.       if( waitpid(rc,&status,WNOHANG)>0 ){
  1896.         isup = 0;
  1897.         break;
  1898.       }
  1899.       sleep(1);
  1900.     }
  1901.     sprintf(zChatStat,"%s/chatstat.%d", DB_DIR, iRec);
  1902.     if( !isup ){
  1903.       char *z = ReadFile(zChatStat);
  1904.       if( z ){
  1905.         WriteStatus(iRec,z);
  1906.       }else{
  1907.         WriteStatus(iRec,"pppd failure at");
  1908.       }
  1909.       unlink(zDeviceFile);
  1910.       unlink(zPppFile);
  1911.     }
  1912.     unlink(zPidFile);
  1913.     unlink(zChatStat);
  1914.     MakeSessionLog(startTime, rc,iRec);
  1915.     PrintStatus(iRec);
  1916.   }
  1917.   return !isup;
  1918. }
  1919.  
  1920.  
  1921. /* This routine is called in order to active the diald daemon.
  1922. */
  1923. static int StartDiald(int iRec){
  1924.   char *zPpp;
  1925.   char *zDiald;
  1926.   char *zPw;
  1927.   char *zUser;
  1928.   char *zService;
  1929.   char *z;
  1930.   int pidPpp;
  1931.   int pidDiald;
  1932.   int rc;
  1933.   int i;
  1934.   FILE *out;
  1935.   char zConfFile[sizeof(DB_DIR)+100];
  1936.   char zPidFile[sizeof(DB_DIR)+20];
  1937.   char zDialdFile[sizeof(DB_DIR)+100];
  1938.   char zServiceBuf[50];
  1939.  
  1940.   /* Check to see if diald is already running.
  1941.   */
  1942.   sprintf(zDialdFile, "%s/diald.%d", DB_DIR, iRec);
  1943.   zDiald = ReadFile(zDialdFile);
  1944.   if( zDiald && (pidDiald=atoi(zDiald))>0 && kill(pidDiald,0)==0 ){
  1945.     return 0;
  1946.   }
  1947.  
  1948.   /* Check to see if we are already running directly.  If so, then
  1949.   ** do a hangup before continuing.
  1950.   */
  1951.   sprintf(zPidFile, "%s/pid.%d", DB_DIR, iRec);
  1952.   zPpp = ReadFile(zPidFile);
  1953.   if( zPpp && (pidPpp=atoi(zPpp))>0 && kill(pidPpp,0)==0 ){
  1954.     Hangup(iRec);
  1955.   }
  1956.  
  1957.   /* Initialize the chap-secrets and pap-secrets files.
  1958.   */
  1959.   sprintf(zServiceBuf,"x%d",iRec);
  1960.   zService = Lookup(iRec,"service",zServiceBuf);
  1961.   zUser = Lookup(iRec,"user","anonymous");
  1962.   zPw = Lookup(iRec, "password", "*");
  1963.   if( zPw ){
  1964.     SetSecrets("pap-secrets",zService,zUser,zPw);
  1965.     SetSecrets("chap-secrets",zService,zUser,zPw);
  1966.   }
  1967.  
  1968.   /* Create the diald configuration script.
  1969.   */
  1970.   sprintf(zConfFile, "%s/diald.conf.%d", DB_DIR, iRec);
  1971.   out = fopen(zConfFile,"w");
  1972.   if( out==0 ){
  1973.     WriteStatus(iRec, "configuration failed");
  1974.     return 1;
  1975.   }
  1976.   fprintf(out,"mode ppp\n");
  1977.   fprintf(out,"device %s\n", Lookup(iRec,"tty","/dev/modem"));
  1978.   fprintf(out,"connect '" SELF " chat %d'\n", iRec);
  1979.   fprintf(out,"speed %s\n", Lookup(iRec,"baud","115200"));
  1980.   z = Lookup(iRec,"defaultroute","y");
  1981.   if( *z=='y' ){
  1982.     fprintf(out,"defaultroute\n");
  1983.   }
  1984.   fprintf(out,"local %s\n", Lookup(iRec,"local","127.0.0.100"));
  1985.   fprintf(out,"remote %s\n", Lookup(iRec,"remote","127.0.0.101"));
  1986.   fprintf(out,
  1987.     "lock\n"
  1988.     "modem\n"
  1989.     "crtscts\n"
  1990.     "-reroute\n"
  1991.     "dynamic\n"
  1992.     "-daemon\n"
  1993.   );
  1994.   fclose(out);
  1995.  
  1996.   /* Fire up the diald process
  1997.   */
  1998.   rc = fork();
  1999.   if( rc<0 ){
  2000.     WriteStatus(iRec,"fork failed");
  2001.     return 1;
  2002.   }
  2003.   if( rc==0 ){
  2004.     /* The child process */
  2005.     int argc = 0;
  2006.     char *argv[30];
  2007.  
  2008.     argv[argc++] = Lookup(iRec, "diald", DIALD_EXE);
  2009.     argv[argc++] = "-f";
  2010.     argv[argc++] = zConfFile;
  2011.     argv[argc++] = "--";
  2012.     argv[argc++] = "remotename";
  2013.     argv[argc++] = zService;
  2014.     argv[argc++] = "user";
  2015.     argv[argc++] = zUser;
  2016.     for(i=0; i<10; i++){
  2017.       char zLabel[100];
  2018.       sprintf(zLabel,"pppopt%d",i);
  2019.       z = Lookup(iRec,zLabel,0);
  2020.       if( z==0 ) break;
  2021.       argv[argc++] = z;
  2022.     }
  2023.     argv[argc] = 0;
  2024.     WriteArgvToTranscript(iRec,argv);
  2025.     close(0);
  2026.     close(1);
  2027.     close(2);
  2028.     open("/dev/null",O_RDWR);
  2029.     open("/dev/null",O_RDWR);
  2030.     open("/dev/null",O_RDWR);
  2031.     execv(argv[0],argv);
  2032.     exit(0);
  2033.   }
  2034.   /* The parent process */
  2035.   WriteToFile(zDialdFile,0644,"%d\n",rc);
  2036.   sleep(1);
  2037.   if( kill(rc,0) ){
  2038.     WriteStatus(iRec,"diald startup failed at");
  2039.     rc = 1;
  2040.   }else{
  2041.     WriteStatus(iRec,"diald started at");
  2042.     rc = 0;
  2043.   }
  2044.   return rc;
  2045. }
  2046.  
  2047. /*
  2048. ** Given a configuration file entry, return a "safe" value for
  2049. ** this entry.  If the entry is the password and the real user
  2050. ** is anything other than root, return "*********".
  2051. */
  2052. static char *SafeValue(ConfEntry *p){
  2053.   char *zVal;
  2054.   if( strcmp(p->zLabel,"password")==0 && getuid()!=0 ){
  2055.     zVal = "**********";
  2056.   }else{
  2057.     zVal = p->zValue;
  2058.   }
  2059.   return zVal;
  2060. }
  2061.  
  2062. /* Print a usage comment and die.  A call to this
  2063. ** routine never returns.
  2064. */
  2065. static void usage(char *argv0){
  2066.   fprintf(stderr,
  2067.     "User commands:\n"
  2068.     "  %s add ORDINAL FIELD=VALUE...\n"
  2069.     "  %s change ORDINAL|SERVICE ?FIELD=VALUE?...\n"
  2070.     "  %s delete ORDINAL|SERVICE|IP\n"
  2071.     "  %s dialdoff ORDINAL|IP|DEVICE\n"
  2072.     "  %s dialdon ORDINAL|IP|DEVICE\n"
  2073.     "  %s down ORDINAL|IP|DEVICE|all\n"
  2074.     "  %s list ?ORDINAL|SERVICE?\n"
  2075.     "  %s log\n"
  2076.     "  %s move ORDINAL ORDINAL\n"
  2077.     "  %s names\n"
  2078.     "  %s status\n"
  2079.     "  %s up SERVICE|IP|ORDINAL\n"
  2080.     "\n"
  2081.     "Commands for internal use only:\n"
  2082.     "  %s chat ORDINAL\n"
  2083.     "  %s ipdown INTERFACE DEVICE SPEED LOCAL-IP REMOTE-IP\n"
  2084.     "  %s ipup INTERFACE DEVICE SPEED LOCAL-IP REMOTE-IP\n"
  2085.     "\n"
  2086.     "Field names:\n"
  2087.     "  phone = phone number for modem to dial\n"
  2088.     "  user = user login name\n"
  2089.     "  password = login password\n"
  2090.     "  tty = serial port for the modem\n"
  2091.     "  baud = baud rate\n"
  2092.     "  service = name of ISP\n"
  2093.     "  chat = \"no\" to ignore \"login:\" prompts, etc.\n"
  2094.     "  autostart = \"no\" to prevent starting pppd if no \"login:\" seen\n"
  2095.     "  pppversion = what version of pppd is being used\n"
  2096.     "  debug = \"yes\" to turn on pppd debugging\n"
  2097.     "  expectN = Nth extra expect string (N between 1 and 9)\n"
  2098.     "  replyN = Reply to issue when expectN is seen\n"
  2099.     "  initN = Nth modem initialization string  (N between 1 and 9)\n"
  2100.     "  diald = name of diald executable\n"
  2101.     "  pppd = name of pppd executable\n"
  2102.     "  local = local IP address for diald slip link\n"
  2103.     "  remote = remote IP address for diald slip link\n"
  2104.     "  idle = number of seconds for idle timeout\n"
  2105.     "  persist = \"yes\" for a permanent connection\n"
  2106.     "  defaultroute = \"no\" to prevent creating a default route\n"
  2107.     "  pppoptN = Nth extra parameter to pppd\n"
  2108.     "  routeN = Add a route to the specified network when pppd comes up\n"
  2109.     "  ip = network that this service gives access to\n"
  2110.     "  netmask = netmask for the ip= network\n"
  2111.     "  dialtimeout = time allowed for the modem to connect\n"
  2112.     "  chattimeout = time allowed for chat responses\n",
  2113.  
  2114.     argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0,
  2115.     argv0, argv0, argv0, argv0, argv0, argv0, argv0
  2116.   );
  2117.   exit(1);
  2118. }
  2119.  
  2120.  
  2121. int main(int argc, char **argv){
  2122.   int iRec;
  2123.   int rc = 0;
  2124.  
  2125.   if( argc<2 ) usage(argv[0]);
  2126.   if( access(DB_DIR,0) ){
  2127.     mkdir(DB_DIR,0755);
  2128.     chown(DB_DIR,0,0);
  2129.     if( access(DB_DIR,0) ){
  2130.       fprintf(stderr,"%s: missing directory \"%s\"\n", argv[0], DB_DIR);
  2131.       exit(1);
  2132.     }
  2133.   }
  2134.   ReadDb();
  2135.   if( (argc==2 || argc==3) && strcmp(argv[1],"list")==0 ){
  2136.     int iRec = FindRecord(argv[2]);
  2137.     Unprivileged();
  2138.     if( iRec<0 || iRec>=nRecord ){
  2139.       int i;
  2140.       for(i=0; i<nRecord; i++){
  2141.         ConfEntry *p = aRecord[i] = sort(aRecord[i]);
  2142.         if( p==0 ) continue;
  2143.         printf("Record %d:\n",i);
  2144.         for(; p; p=p->next){
  2145.           printf("  %s = %s\n",p->zLabel, SafeValue(p));
  2146.         }
  2147.       }
  2148.     }else{
  2149.       ConfEntry *p = aRecord[iRec] = sort(aRecord[iRec]);
  2150.       for(; p; p=p->next){
  2151.         printf("  %s = %s\n",p->zLabel, SafeValue(p));
  2152.       }
  2153.     }    
  2154.   }else if( argc==2 && strcmp(argv[1],"log")==0 ){
  2155.     char *z;
  2156.     Unprivileged();
  2157.     z = ReadFile(SESSIONLOG);
  2158.     if( z ){
  2159.       printf("%s",z);
  2160.     }
  2161.   }else if( (argc==2 || argc==3 ) && strcmp(argv[1],"tcllist")==0 ){
  2162.     int i;
  2163.     Unprivileged();
  2164.     for(i=0; i<nRecord; i++){
  2165.       ConfEntry *p = aRecord[i] = sort(aRecord[i]);
  2166.       if( p==0 ) continue;
  2167.       for(; p; p=p->next){
  2168.         printf("set eznet(%d:%s) ",i,p->zLabel);
  2169.         WriteTcl(stdout,SafeValue(p));
  2170.         printf("\n");
  2171.       }
  2172.     }
  2173.   }else if( argc==2 && strcmp(argv[1],"names")==0 ){
  2174.     int i;
  2175.     Unprivileged();
  2176.     for(i=0; i<nRecord; i++){
  2177.       char *z = Lookup(i,"service",0);
  2178.       if( z ){ 
  2179.         printf("%s\n",z);
  2180.       }else{
  2181.         printf("%d\n",i);
  2182.       }
  2183.     }
  2184.   }else if( argc>=3 && strcmp(argv[1],"add")==0 ){
  2185.     int iRec;
  2186.     Unprivileged();
  2187.     iRec = FindRecord(argv[2]);
  2188.     if( iRec==FIND_ERROR ){
  2189.       AddEntries(nRecord, argc-2, argv+2);
  2190.     }else if( iRec>=nRecord ){
  2191.       AddEntries(nRecord, argc-3, argv+3);
  2192.     }else{
  2193.       AddEntries(nRecord, argc-3, argv+3);
  2194.       MoveRecord(nRecord, iRec);
  2195.     }
  2196.     WriteDb();
  2197.   }else if( argc==3 && strcmp(argv[1],"delete")==0 ){
  2198.     int iRec = FindRecord(argv[2]);
  2199.     Unprivileged();
  2200.     if( iRec==FIND_ALL ){
  2201.       nRecord = 0;
  2202.     }else if( iRec==FIND_ERROR ){
  2203.       usage(argv[0]);
  2204.     }else{
  2205.       MoveRecord(iRec,nRecord-1);
  2206.       aRecord[nRecord-1] = 0;
  2207.     }
  2208.     WriteDb();
  2209.   }else if( argc>=4 && strcmp(argv[1],"change")==0 ){
  2210.     int iRec;
  2211.     Unprivileged();
  2212.     iRec = FindRecord(argv[2]);
  2213.     if( iRec==FIND_ALL ){
  2214.       usage(argv[0]);
  2215.     }else{
  2216.       if( iRec==FIND_ERROR ){
  2217.         iRec = nRecord==1 ? 0 : nRecord;
  2218.       }
  2219.       AddEntries(iRec, argc-3, argv+3);
  2220.     }
  2221.     WriteDb();
  2222.   }else if( argc==4 && strcmp(argv[1],"move")==0 ){
  2223.     int from, to;
  2224.     Unprivileged();
  2225.     if( argc!=4 ) usage(argv[0]);
  2226.     from = GetInt(argv[2]);
  2227.     to = GetInt(argv[3]);
  2228.     if( from>=0 && to>=0 ) MoveRecord(from,to);
  2229.     WriteDb();
  2230.   }else if( argc==3 && strcmp(argv[1],"chat")==0 ){
  2231.     iRec = FindRecord(argv[2]);
  2232.     if( iRec<0 ) usage(argv[0]);
  2233.     rc = Chat(iRec);
  2234.   }else if( (argc==2 || argc==3) && strcmp(argv[1],"status")==0 ){
  2235.     iRec = FindRecord(argv[2]);
  2236.     Unprivileged();
  2237.     if( iRec<0 ){
  2238.       for(iRec=0; iRec<nRecord; iRec++){
  2239.         PrintStatus(iRec);
  2240.       }
  2241.     }else{
  2242.       PrintStatus(iRec);
  2243.     }
  2244.   }else if( (argc==2 || argc==3) && strcmp(argv[1],"down")==0 ){
  2245.     iRec = FindRecord(argv[2]);
  2246.     /*  Unprivileged();  --- Yeah.  Let anybody do a hangup... */
  2247.     if( iRec==FIND_ALL ){
  2248.       system("/bin/kill -TERM `/sbin/pidof pppd` >/dev/null 2>&1");
  2249.       system("/bin/kill -TERM `/sbin/pidof diald` >/dev/null 2>&1");
  2250.       for(iRec=0; iRec<nRecord; iRec++){
  2251.         Hangup(iRec);
  2252.       }
  2253.     }else{
  2254.       Hangup(iRec);
  2255.     }
  2256.   }else if( argc==7 && strcmp(argv[1],"ipup")==0 ){
  2257.     Unprivileged();
  2258.     IpUp(&argv[2]);
  2259.   }else if( argc==7 && strcmp(argv[1],"ipdown")==0 ){
  2260.     Unprivileged();
  2261.     IpDown(&argv[2]);
  2262.   }else if( (argc==2 || argc==3) && strcmp(argv[1],"up")==0 ){
  2263.     if( access(SELF,X_OK)!=0 ){
  2264.       fprintf(stderr,"eznet must be installed as \"" SELF "\"\n");
  2265.       exit(1);
  2266.     }
  2267.     iRec = FindRecord(argv[2]);
  2268.     if( iRec<0 ){ usage(argv[0]); }
  2269.     rc = Dial(iRec);
  2270.   }else if( (argc==3 || argc==2) && strcmp(argv[1],"dialdon")==0 ){
  2271.     if( access(SELF,X_OK)!=0 ){
  2272.       fprintf(stderr,"eznet must be installed as \"" SELF "\"\n");
  2273.       exit(1);
  2274.     }
  2275.     iRec = FindRecord(argv[2]);
  2276.     if( iRec<0 ){ usage(argv[0]); }
  2277.     rc = StartDiald(iRec);
  2278.   }else if( (argc==3 || argc==2) && strcmp(argv[1],"dialdoff")==0 ){
  2279.     iRec = FindRecord(argv[2]);
  2280.     if( iRec<0 ){ usage(argv[0]); }
  2281.     rc = StopDiald(iRec);
  2282.   }else if( argc==3 && strcmp(argv[1],"delete")==0 ){
  2283.     Unprivileged();
  2284.     iRec = FindRecord(argv[2]);
  2285.     if( iRec>=0 && iRec<nRecord ){
  2286.       MoveRecord(iRec, nRecord-1);
  2287.       nRecord--;
  2288.       WriteDb();
  2289.     }
  2290.   }else{
  2291.     Unprivileged();
  2292.     usage(argv[0]);
  2293.   }
  2294.   return rc;
  2295. }
  2296.